/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2023-2023.
 * Description: reset hdlc in booting
 * Author: pengyunlong <pengyunlong@huawei.com>
 * Create: 2023-11-18
 */

#include <linux/types.h>
#include <linux/libfdt.h>
#include <mach/be8_compatible.h>
#include <asm/io.h>
#include "rtos_parse_cmdline.h"

#define local_cpu_relax() ({ __asm__ __volatile__("" : : : "memory"); })

/* test on HI1210, local_cpu_relax 85000 times will take 1ms */
#define RELAX_CNT 85000

static void do_hdlc_reset(unsigned long addr, int mask)
{
	int tmp;
	int i;
	/*
	 * the way to shutdown hdlc is provieded by marp
	 * and has been verifyed on V3_FDD
	 */
	IO_READ(addr, tmp);

	tmp |= mask;
	io_write(addr, tmp);

	/* delay 1ms by marp requirement */
	for (i = 0; i < RELAX_CNT; i++)
		local_cpu_relax();

	tmp &= ~mask;
	io_write(addr, tmp);
}

void v3fdd_hdlc_reset(void *dtb)
{
	int node;
	char *bootargs;
	char *p;
	int size;
	unsigned int addr;
	unsigned int mask;

	node = fdt_subnode_offset(dtb, 0, "chosen");
	if (node < 0)
		return;

	bootargs = (char *)fdt_getprop(dtb, node, "bootargs", &size);
	if (bootargs == NULL)
		return;

	p = find_str_in_bootargs(bootargs, "hdlc_reset");
	if (p) {
		p += strlen("hdlc_reset");
		addr = parse_mem(p, &size);
		p += size;
		mask = parse_mem(p, &size);
		if (addr)
			do_hdlc_reset(addr, mask);
	}
}
